1//////////////////////////////////////////////////////////////////////
2// LibFile: shapes.scad
3// Common useful shapes and structured objects.
4// To use, add the following lines to the beginning of your file:
5// ```
6// include <BOSL/constants.scad>
7// use <BOSL/shapes.scad>
8// ```
9//////////////////////////////////////////////////////////////////////
10
11/*
12BSD 2-Clause License
13
14Copyright (c) 2017, Revar Desmera
15All rights reserved.
16
17Redistribution and use in source and binary forms, with or without
18modification, are permitted provided that the following conditions are met:
19
20* Redistributions of source code must retain the above copyright notice, this
21 list of conditions and the following disclaimer.
22
23* Redistributions in binary form must reproduce the above copyright notice,
24 this list of conditions and the following disclaimer in the documentation
25 and/or other materials provided with the distribution.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
31FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
34CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
35OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37*/
38
39
40use <transforms.scad>
41use <math.scad>
42include <compat.scad>
43include <constants.scad>
44
45
46// Section: Cuboids
47
48
49// Module: cuboid()
50//
51// Description:
52// Creates a cube or cuboid object, with optional chamfering or filleting/rounding.
53//
54// Arguments:
55// size = The size of the cube.
56// chamfer = Size of chamfer, inset from sides. Default: No chamferring.
57// fillet = Radius of fillet for edge rounding. Default: No filleting.
58// edges = Edges to chamfer/fillet. Use `EDGE` constants from constants.scad. Default: `EDGES_ALL`
59// trimcorners = If true, rounds or chamfers corners where three chamferred/filleted edges meet. Default: `true`
60// p1 = Align the cuboid's corner at `p1`, if given. Forces `align=V_UP+V_BACK+V_RIGHT`.
61// p2 = If given with `p1`, defines the cornerpoints of the cuboid.
62// align = The side of the origin to align to. Use `V_` constants from `constants.scad`. Default: `V_CENTER`
63// center = If given, overrides `align`. A true value sets `align=V_CENTER`, false sets `align=V_UP+V_BACK+V_RIGHT`.
64//
65// Example: Simple regular cube.
66// cuboid(40);
67// Example: Cube with minimum cornerpoint given.
68// cuboid(20, p1=[10,0,0]);
69// Example: Rectangular cube, with given X, Y, and Z sizes.
70// cuboid([20,40,50]);
71// Example: Rectangular cube defined by opposing cornerpoints.
72// cuboid(p1=[0,10,0], p2=[20,30,30]);
73// Example: Rectangular cube with chamferred edges and corners.
74// cuboid([30,40,50], chamfer=5);
75// Example: Rectangular cube with chamferred edges, without trimmed corners.
76// cuboid([30,40,50], chamfer=5, trimcorners=false);
77// Example: Rectangular cube with rounded edges and corners.
78// cuboid([30,40,50], fillet=10);
79// Example: Rectangular cube with rounded edges, without trimmed corners.
80// cuboid([30,40,50], fillet=10, trimcorners=false);
81// Example: Rectangular cube with only some edges chamferred.
82// cuboid([30,40,50], chamfer=5, edges=EDGE_TOP_FR+EDGE_TOP_RT+EDGE_FR_RT, $fn=24);
83// Example: Rectangular cube with only some edges rounded.
84// cuboid([30,40,50], fillet=5, edges=EDGE_TOP_FR+EDGE_TOP_RT+EDGE_FR_RT, $fn=24);
85module cuboid(
86 size=[1,1,1],
87 p1=undef, p2=undef,
88 chamfer=undef,
89 fillet=undef,
90 edges=EDGES_ALL,
91 trimcorners=true,
92 align=[0,0,0],
93 center=undef
94) {
95 size = scalar_vec3(size);
96 if (is_def(p1)) {
97 if (is_def(p2)) {
98 translate([for (v=array_zip([p1,p2],fill=0)) min(v)]) {
99 cuboid(size=vabs(p2-p1), chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=V_ALLPOS);
100 }
101 } else {
102 translate(p1) {
103 cuboid(size=size, chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=V_ALLPOS);
104 }
105 }
106 } else {
107 majrots = [[0,90,0], [90,0,0], [0,0,0]];
108
109 // Not the most elegant, but should work fine.
110 // Size for edge E can be ignored if there are only zeros in both edges !E
111 relevantsize = [for(a=[0:2]) if(max(edges[(a+1)%3]+edges[(a+2)%3])>0) size[a]];
112
113 if (chamfer != undef && len(relevantsize) > 0) assertion(chamfer <= min(relevantsize)/2, "chamfer must be smaller than half the cube width, length, or height.");
114 if (fillet != undef && len(relevantsize) > 0 ) assertion(fillet <= min(relevantsize)/2, "fillet must be smaller than half the cube width, length, or height.");
115 algn = (!is_def(center))? (is_scalar(align)? align*V_UP : align) : (center==true)? V_CENTER : V_ALLPOS;
116 translate(vmul(size/2, algn)) {
117 if (chamfer != undef) {
118 isize = [for (v = size) max(0.001, v-2*chamfer)];
119 if (edges == EDGES_ALL && trimcorners) {
120 hull() {
121 cube([size[0], isize[1], isize[2]], center=true);
122 cube([isize[0], size[1], isize[2]], center=true);
123 cube([isize[0], isize[1], size[2]], center=true);
124 }
125 } else {
126 difference() {
127 cube(size, center=true);
128
129 // Chamfer edges
130 for (i = [0:3], axis=[0:2]) {
131 if (edges[axis][i]>0) {
132 translate(vmul(EDGE_OFFSETS[axis][i], size/2)) {
133 rotate(majrots[axis]) {
134 zrot(45) cube([chamfer*sqrt(2), chamfer*sqrt(2), size[axis]+0.01], center=true);
135 }
136 }
137 }
138 }
139
140 // Chamfer triple-edge corners.
141 if (trimcorners) {
142 for (za=[-1,1], ya=[-1,1], xa=[-1,1]) {
143 if (corner_edge_count(edges, [xa,ya,za]) > 2) {
144 translate(vmul([xa,ya,za]/2, size-[1,1,1]*chamfer*4/3)) {
145 rot(from=V_UP, to=[xa,ya,za]) {
146 upcube(chamfer*3);
147 }
148 }
149 }
150 }
151 }
152 }
153 }
154 } else if (fillet != undef) {
155 sides = quantup(segs(fillet),4);
156 sc = 1/cos(180/sides);
157 isize = [for (v = size) max(0.001, v-2*fillet)];
158 if (edges == EDGES_ALL) {
159 minkowski() {
160 cube(isize, center=true);
161 if (trimcorners) {
162 rotate_extrude(convexity=2,$fn=sides) {
163 polygon([for (i=[0:1:sides/2]) let(a=i*360/sides-90) fillet*sc*[cos(a),sin(a)]]);
164 }
165 } else {
166 intersection() {
167 zrot(180/sides) cylinder(r=fillet*sc, h=fillet*2, center=true, $fn=sides);
168 rotate([90,0,0]) zrot(180/sides) cylinder(r=fillet*sc, h=fillet*2, center=true, $fn=sides);
169 rotate([0,90,0]) zrot(180/sides) cylinder(r=fillet*sc, h=fillet*2, center=true, $fn=sides);
170 }
171 }
172 }
173 } else {
174 difference() {
175 cube(size, center=true);
176
177 // Round edges.
178 for (i = [0:3], axis=[0:2]) {
179 if (edges[axis][i]>0) {
180 difference() {
181 translate(vmul(EDGE_OFFSETS[axis][i], size/2)) {
182 rotate(majrots[axis]) cube([fillet*2, fillet*2, size[axis]+0.1], center=true);
183 }
184 translate(vmul(EDGE_OFFSETS[axis][i], size/2 - [1,1,1]*fillet)) {
185 rotate(majrots[axis]) zrot(180/sides) cylinder(h=size[axis]+0.2, r=fillet*sc, center=true, $fn=sides);
186 }
187 }
188 }
189 }
190
191 // Round triple-edge corners.
192 if (trimcorners) {
193 for (za=[-1,1], ya=[-1,1], xa=[-1,1]) {
194 if (corner_edge_count(edges, [xa,ya,za]) > 2) {
195 difference() {
196 translate(vmul([xa,ya,za], size/2)) {
197 cube(fillet*2, center=true);
198 }
199 translate(vmul([xa,ya,za], size/2-[1,1,1]*fillet)) {
200 zrot(180/sides) sphere(r=fillet*sc*sc, $fn=sides);
201 }
202 }
203 }
204 }
205 }
206 }
207 }
208 } else {
209 cube(size=size, center=true);
210 }
211 }
212 }
213}
214
215
216
217// Module: cube2pt()
218// Status: DEPRECATED, use `cuboid(p1,p2)` instead.
219//
220// Usage:
221// cube2pt(p1,p2)
222//
223// Description:
224// Creates a cube between two points.
225//
226// Arguments:
227// p1 = Coordinate point of one cube corner.
228// p2 = Coordinate point of opposite cube corner.
229module cube2pt(p1,p2) {
230 deprecate("cube2pt()", "cuboid(p1,p2)");
231 cuboid(p1=p1, p2=p2);
232}
233
234
235
236// Module: span_cube()
237//
238// Description:
239// Creates a cube that spans the X, Y, and Z ranges given.
240//
241// Arguments:
242// xspan = [min, max] X axis range.
243// yspan = [min, max] Y axis range.
244// zspan = [min, max] Z axis range.
245//
246// Example:
247// span_cube([0,15], [5,10], [0, 10]);
248module span_cube(xspan, yspan, zspan) {
249 span = [xspan, yspan, zspan];
250 cuboid(p1=array_subindex(span,0), p2=array_subindex(span,1));
251}
252
253
254
255// Module: offsetcube()
256// Status: DEPRECATED, use `cuboid(..., align)` instead.
257//
258// Description:
259// Makes a cube that is offset along the given vector by half the cube's size.
260// For example, if `v=[-1,1,0]`, the cube's front right edge will be centered at the origin.
261//
262// Arguments:
263// size = size of cube.
264// v = vector to offset along.
265module offsetcube(size=[1,1,1], v=[0,0,0]) {
266 deprecate("offsetcube()", "cuboid()");
267 cuboid(size=size, align=v);
268}
269
270
271// Module: leftcube()
272//
273// Description:
274// Makes a cube that is aligned on the left side of the origin.
275//
276// Usage:
277// leftcube(size);
278//
279// Arguments:
280// size = The size of the cube to make.
281//
282// Example:
283// leftcube([20,30,40]);
284module leftcube(size=[1,1,1]) {siz = scalar_vec3(size); left(siz[0]/2) cube(size=size, center=true);}
285
286
287// Module: rightcube()
288//
289// Description:
290// Makes a cube that is aligned on the right side of the origin.
291//
292// Usage:
293// rightcube(size);
294//
295// Arguments:
296// size = The size of the cube to make.
297//
298// Example:
299// rightcube([20,30,40]);
300module rightcube(size=[1,1,1]) {siz = scalar_vec3(size); right(siz[0]/2) cube(size=size, center=true);}
301
302
303// Module: fwdcube()
304//
305// Description:
306// Makes a cube that is aligned on the front side of the origin.
307//
308// Usage:
309// fwdcube(size);
310//
311// Arguments:
312// size = The size of the cube to make.
313//
314// Example:
315// fwdcube([20,30,40]);
316module fwdcube(size=[1,1,1]) {siz = scalar_vec3(size); fwd(siz[1]/2) cube(size=size, center=true);}
317
318
319// Module: backcube()
320//
321// Description:
322// Makes a cube that is aligned on the front side of the origin.
323//
324// Usage:
325// backcube(size);
326//
327// Arguments:
328// size = The size of the cube to make.
329//
330// Example:
331// backcube([20,30,40]);
332module backcube(size=[1,1,1]) {siz = scalar_vec3(size); back(siz[1]/2) cube(size=size, center=true);}
333
334
335// Module: downcube()
336//
337// Description:
338// Makes a cube that is aligned on the bottom side of the origin.
339//
340// Usage:
341// downcube(size);
342//
343// Arguments:
344// size = The size of the cube to make.
345//
346// Example:
347// downcube([20,30,40]);
348module downcube(size=[1,1,1]) {siz = scalar_vec3(size); down(siz[2]/2) cube(size=size, center=true);}
349
350
351// Module: upcube()
352//
353// Description:
354// Makes a cube that is aligned on the top side of the origin.
355//
356// Usage:
357// upcube(size);
358//
359// Arguments:
360// size = The size of the cube to make.
361//
362// Example:
363// upcube([20,30,40]);
364module upcube(size=[1,1,1]) {siz = scalar_vec3(size); up(siz[2]/2) cube(size=size, center=true);}
365
366
367// Module: chamfcube()
368// Status: DEPRECATED, use `cuboid(..., chamfer, edges, trimcorners)` instead.
369//
370// Description:
371// Makes a cube with chamfered edges.
372//
373// Arguments:
374// size = Size of cube [X,Y,Z]. (Default: `[1,1,1]`)
375// chamfer = Chamfer inset along axis. (Default: `0.25`)
376// chamfaxes = Array [X,Y,Z] of boolean values to specify which axis edges should be chamfered.
377// chamfcorners = Boolean to specify if corners should be flat chamferred.
378module chamfcube(size=[1,1,1], chamfer=0.25, chamfaxes=[1,1,1], chamfcorners=false) {
379 deprecate("chamfcube()", "cuboid()");
380 cuboid(
381 size=size,
382 chamfer=chamfer,
383 trimcorners=chamfcorners,
384 edges = (
385 (chamfaxes[0]? EDGES_X_ALL : EDGES_NONE) +
386 (chamfaxes[1]? EDGES_Y_ALL : EDGES_NONE) +
387 (chamfaxes[2]? EDGES_Z_ALL : EDGES_NONE)
388 )
389 );
390}
391
392
393// Module: rrect()
394// Status: DEPRECATED, use `cuboid(..., fillet, edges)` instead.
395//
396// Description:
397// Makes a cube with rounded (filletted) vertical edges. The `r` size will be
398// limited to a maximum of half the length of the shortest XY side.
399//
400// Arguments:
401// size = Size of cube [X,Y,Z]. (Default: `[1,1,1]`)
402// r = Radius of edge/corner rounding. (Default: `0.25`)
403// center = If true, object will be centered. If false, sits on top of XY plane.
404module rrect(size=[1,1,1], r=0.25, center=false) {
405 deprecate("rrect()", "cuboid()");
406 cuboid(size=size, fillet=r, edges=EDGES_Z_ALL, align=center? V_CENTER : V_UP);
407}
408
409
410// Module: rcube()
411// Status: DEPRECATED, use `cuboid(..., fillet)` instead.
412//
413// Description:
414// Makes a cube with rounded (filletted) edges and corners. The `r` size will be
415// limited to a maximum of half the length of the shortest cube side.
416//
417// Arguments:
418// size = Size of cube [X,Y,Z]. (Default: `[1,1,1]`)
419// r = Radius of edge/corner rounding. (Default: `0.25`)
420// center = If true, object will be centered. If false, sits on top of XY plane.
421module rcube(size=[1,1,1], r=0.25, center=false) {
422 deprecate("rcube()", "cuboid()");
423 cuboid(size=size, fillet=r, align=center? V_CENTER : V_UP);
424}
425
426
427
428// Section: Prismoids
429
430
431// Module: prismoid()
432//
433// Description:
434// Creates a rectangular prismoid shape.
435//
436// Usage:
437// prismoid(size1, size2, h, [shift], [orient], [align|center]);
438//
439// Arguments:
440// size1 = [width, length] of the axis-negative end of the prism.
441// size2 = [width, length] of the axis-positive end of the prism.
442// h = Height of the prism.
443// shift = [x, y] amount to shift the center of the top with respect to the center of the bottom.
444// orient = Orientation of the prismoid. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
445// align = Alignment of the prismoid by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `ALIGN_POS`.
446// center = If given, overrides `align`. A true value sets `align=V_CENTER`, false sets `align=ALIGN_POS`.
447//
448// Example: Rectangular Pyramid
449// prismoid(size1=[40,40], size2=[0,0], h=20);
450// Example: Prism
451// prismoid(size1=[40,40], size2=[0,40], h=20);
452// Example: Truncated Pyramid
453// prismoid(size1=[35,50], size2=[20,30], h=20);
454// Example: Wedge
455// prismoid(size1=[60,35], size2=[30,0], h=30);
456// Example: Truncated Tetrahedron
457// prismoid(size1=[10,40], size2=[40,10], h=40);
458// Example: Inverted Truncated Pyramid
459// prismoid(size1=[15,5], size2=[30,20], h=20);
460// Example: Right Prism
461// prismoid(size1=[30,60], size2=[0,60], shift=[-15,0], h=30);
462// Example(FlatSpin): Shifting/Skewing
463// prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]);
464module prismoid(
465 size1=[1,1], size2=[1,1], h=1, shift=[0,0],
466 orient=ORIENT_Z, align=ALIGN_POS, center=undef
467) {
468 eps = 0.001;
469 s1 = [max(size1[0], eps), max(size1[1], eps)];
470 s2 = [max(size2[0], eps), max(size2[1], eps)];
471 shiftby = point3d(shift);
472 orient_and_align([s1[0], s1[1], h], orient, align, center, noncentered=ALIGN_POS) {
473 polyhedron(
474 points=[
475 [+s2[0]/2, +s2[1]/2, +h/2] + shiftby,
476 [+s2[0]/2, -s2[1]/2, +h/2] + shiftby,
477 [-s2[0]/2, -s2[1]/2, +h/2] + shiftby,
478 [-s2[0]/2, +s2[1]/2, +h/2] + shiftby,
479 [+s1[0]/2, +s1[1]/2, -h/2],
480 [+s1[0]/2, -s1[1]/2, -h/2],
481 [-s1[0]/2, -s1[1]/2, -h/2],
482 [-s1[0]/2, +s1[1]/2, -h/2],
483 ],
484 faces=[
485 [0, 1, 2],
486 [0, 2, 3],
487 [0, 4, 5],
488 [0, 5, 1],
489 [1, 5, 6],
490 [1, 6, 2],
491 [2, 6, 7],
492 [2, 7, 3],
493 [3, 7, 4],
494 [3, 4, 0],
495 [4, 7, 6],
496 [4, 6, 5],
497 ],
498 convexity=2
499 );
500 }
501}
502
503
504// Module: trapezoid()
505// Status: DEPRECATED, use `prismoid()` instead.
506//
507// Description:
508// Creates a rectangular prismoid shape.
509//
510// Usage:
511// trapezoid(size1, size2, h, [shift], [orient], [align|center]);
512//
513// Arguments:
514// size1 = [width, length] of the axis-negative end of the prism.
515// size2 = [width, length] of the axis-positive end of the prism.
516// h = Height of the prism.
517// shift = [x, y] amount to shift the center of the top with respect to the center of the bottom.
518// orient = Orientation of the prismoid. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
519// align = Alignment of the prismoid by the axis-negative (size1) end. Use the `V_` constants from `constants.scad`. Default: `V_UP`
520// center = If given, overrides `align`. A true value sets `align=V_CENTER`, false sets `align=V_UP`.
521module trapezoid(size1=[1,1], size2=[1,1], h=1, center=false) {
522 deprecate("trapezoid()", "prismoid()");
523 prismoid(size1=size1, size2=size2, h=h, center=center);
524}
525
526
527// Module: rounded_prismoid()
528//
529// Description:
530// Creates a rectangular prismoid shape with rounded vertical edges.
531//
532// Arguments:
533// size1 = [width, length] of the bottom of the prism.
534// size2 = [width, length] of the top of the prism.
535// h = Height of the prism.
536// r = radius of vertical edge fillets.
537// r1 = radius of vertical edge fillets at bottom.
538// r2 = radius of vertical edge fillets at top.
539// shift = [x, y] amount to shift the center of the top with respect to the center of the bottom.
540// orient = Orientation of the prismoid. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
541// align = Alignment of the prismoid by the axis-negative (`size1`) end. Use the `V_` constants from `constants.scad`. Default: `V_UP`.
542// center = vertically center the prism. Overrides `align`.
543//
544// Example: Rounded Pyramid
545// rounded_prismoid(size1=[40,40], size2=[0,0], h=25, r=5);
546// Example: Centered Rounded Pyramid
547// rounded_prismoid(size1=[40,40], size2=[0,0], h=25, r=5, center=true);
548// Example: Disparate Top and Bottom Radii
549// rounded_prismoid(size1=[40,60], size2=[40,60], h=20, r1=3, r2=10, $fn=24);
550// Example(FlatSpin): Shifting/Skewing
551// rounded_prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5], r=5);
552module rounded_prismoid(
553 size1, size2, h, shift=[0,0],
554 r=undef, r1=undef, r2=undef,
555 align=V_UP, orient=ORIENT_Z, center=undef
556) {
557 eps = 0.001;
558 maxrad1 = min(size1[0]/2, size1[1]/2);
559 maxrad2 = min(size2[0]/2, size2[1]/2);
560 rr1 = min(maxrad1, (r1!=undef)? r1 : r);
561 rr2 = min(maxrad2, (r2!=undef)? r2 : r);
562 shiftby = point3d(shift);
563 orient_and_align([size1.x, size1.y, h], orient, align, center, noncentered=ALIGN_POS) {
564 down(h/2)
565 hull() {
566 linear_extrude(height=eps, center=false, convexity=2) {
567 offset(r=rr1) {
568 square([max(eps, size1[0]-2*rr1), max(eps, size1[1]-2*rr1)], center=true);
569 }
570 }
571 up(h-0.01) {
572 translate(shiftby) {
573 linear_extrude(height=eps, center=false, convexity=2) {
574 offset(r=rr2) {
575 square([max(eps, size2[0]-2*rr2), max(eps, size2[1]-2*rr2)], center=true);
576 }
577 }
578 }
579 }
580 }
581 }
582}
583
584
585
586// Module: pyramid()
587// Status: DEPRECATED, use `cyl(, r2=0, $fn=N)` instead.
588//
589// Usage:
590// pyramid(n, h, l|r|d, [circum]);
591//
592// Description:
593// Creates a pyramidal prism with a given number of sides.
594//
595// Arguments:
596// n = number of pyramid sides.
597// h = height of the pyramid.
598// l = length of one side of the pyramid. (optional)
599// r = radius of the base of the pyramid. (optional)
600// d = diameter of the base of the pyramid. (optional)
601// circum = base circumscribes the circle of the given radius or diam.
602module pyramid(n=4, h=1, l=1, r=undef, d=undef, circum=false)
603{
604 deprecate("pyramid()", "cyl()");
605 radius = get_radius(r=r, d=d, dflt=l/2/sin(180/n));
606 cyl(r1=radius, r2=0, l=h, circum=circum, $fn=n, realign=true, align=ALIGN_POS);
607}
608
609
610// Module: prism()
611// Status: DEPRECATED, use `cyl(..., $fn=N)` instead.
612//
613// Usage:
614// prism(n, h, l|r|d, [circum]);
615//
616// Description:
617// Creates a vertical prism with a given number of sides.
618//
619// Arguments:
620// n = number of sides.
621// h = height of the prism.
622// l = length of one side of the prism. (optional)
623// r = radius of the prism. (optional)
624// d = diameter of the prism. (optional)
625// circum = prism circumscribes the circle of the given radius or diam.
626module prism(n=3, h=1, l=1, r=undef, d=undef, circum=false, center=false)
627{
628 deprecate("prism()", "cyl()");
629 radius = get_radius(r=r, d=d, dflt=l/2/sin(180/n));
630 cyl(r=radius, l=h, circum=circum, $fn=n, realign=true, center=center);
631}
632
633
634// Module: right_triangle()
635//
636// Description:
637// Creates a 3D right triangular prism.
638//
639// Usage:
640// right_triangle(size, [orient], [align|center]);
641//
642// Arguments:
643// size = [width, thickness, height]
644// orient = The axis to place the hypotenuse along. Only accepts `ORIENT_X`, `ORIENT_Y`, or `ORIENT_Z` from `constants.scad`. Default: `ORIENT_Y`.
645// align = The side of the origin to align to. Use `V_` constants from `constants.scad`. Default: `V_UP+V_BACK+V_RIGHT`.
646// center = If given, overrides `align`. A true value sets `align=V_CENTER`, false sets `align=V_UP+V_BACK+V_RIGHT`.
647//
648// Example: Centered
649// right_triangle([60, 10, 40], center=true);
650// Example: *Non*-Centered
651// right_triangle([60, 10, 40]);
652module right_triangle(size=[1, 1, 1], orient=ORIENT_Y, align=V_ALLPOS, center=undef)
653{
654 siz = scalar_vec3(size);
655 orient_and_align(siz, align=align, center=center) {
656 if (orient == ORIENT_X) {
657 ang = atan2(siz[1], siz[2]);
658 masksize = [siz[0], siz[1], norm([siz[1],siz[2]])] + [1,1,1];
659 xrot(ang) {
660 difference() {
661 xrot(-ang) cube(siz, center=true);
662 back(masksize[1]/2) cube(masksize, center=true);
663 }
664 }
665 } else if (orient == ORIENT_Y) {
666 ang = atan2(siz[0], siz[2]);
667 masksize = [siz[0], siz[1], norm([siz[0],siz[2]])] + [1,1,1];
668 yrot(-ang) {
669 difference() {
670 yrot(ang) cube(siz, center=true);
671 right(masksize[0]/2) cube(masksize, center=true);
672 }
673 }
674 } else if (orient == ORIENT_Z) {
675 ang = atan2(siz[0], siz[1]);
676 masksize = [norm([siz[0],siz[1]]), siz[1], siz[2]] + [1,1,1];
677 zrot(-ang) {
678 difference() {
679 zrot(ang) cube(siz, center=true);
680 back(masksize[1]/2) cube(masksize, center=true);
681 }
682 }
683 }
684 }
685}
686
687
688
689// Section: Cylindroids
690
691
692// Module: cyl()
693//
694// Description:
695// Creates cylinders in various alignments and orientations,
696// with optional fillets and chamfers. You can use `r` and `l`
697// interchangably, and all variants allow specifying size
698// by either `r`|`d`, or `r1`|`d1` and `r2`|`d2`.
699// Note that that chamfers and fillets cannot cross the
700// midpoint of the cylinder's length.
701//
702// Usage: Normal Cylinders
703// cyl(l|h, r|d, [circum], [realign], [orient], [align], [center]);
704// cyl(l|h, r1|d1, r2/d2, [circum], [realign], [orient], [align], [center]);
705//
706// Usage: Chamferred Cylinders
707// cyl(l|h, r|d, chamfer, [chamfang], [from_end], [circum], [realign], [orient], [align], [center]);
708// cyl(l|h, r|d, chamfer1, [chamfang1], [from_end], [circum], [realign], [orient], [align], [center]);
709// cyl(l|h, r|d, chamfer2, [chamfang2], [from_end], [circum], [realign], [orient], [align], [center]);
710// cyl(l|h, r|d, chamfer1, chamfer2, [chamfang1], [chamfang2], [from_end], [circum], [realign], [orient], [align], [center]);
711//
712// Usage: Rounded/Filleted Cylinders
713// cyl(l|h, r|d, fillet, [circum], [realign], [orient], [align], [center]);
714// cyl(l|h, r|d, fillet1, [circum], [realign], [orient], [align], [center]);
715// cyl(l|h, r|d, fillet2, [circum], [realign], [orient], [align], [center]);
716// cyl(l|h, r|d, fillet1, fillet2, [circum], [realign], [orient], [align], [center]);
717//
718// Arguments:
719// l / h = Length of cylinder along oriented axis. (Default: 1.0)
720// r = Radius of cylinder.
721// r1 = Radius of the negative (X-, Y-, Z-) end of cylinder.
722// r2 = Radius of the positive (X+, Y+, Z+) end of cylinder.
723// d = Diameter of cylinder.
724// d1 = Diameter of the negative (X-, Y-, Z-) end of cylinder.
725// d2 = Diameter of the positive (X+, Y+, Z+) end of cylinder.
726// circum = If true, cylinder should circumscribe the circle of the given size. Otherwise inscribes. Default: `false`
727// chamfer = The size of the chamfers on the ends of the cylinder. Default: none.
728// chamfer1 = The size of the chamfer on the axis-negative end of the cylinder. Default: none.
729// chamfer2 = The size of the chamfer on the axis-positive end of the cylinder. Default: none.
730// chamfang = The angle in degrees of the chamfers on the ends of the cylinder.
731// chamfang1 = The angle in degrees of the chamfer on the axis-negative end of the cylinder.
732// chamfang2 = The angle in degrees of the chamfer on the axis-positive end of the cylinder.
733// from_end = If true, chamfer is measured from the end of the cylinder, instead of inset from the edge. Default: `false`.
734// fillet = The radius of the fillets on the ends of the cylinder. Default: none.
735// fillet1 = The radius of the fillet on the axis-negative end of the cylinder.
736// fillet2 = The radius of the fillet on the axis-positive end of the cylinder.
737// realign = If true, rotate the cylinder by half the angle of one face.
738// orient = Orientation of the cylinder. Use the `ORIENT_` constants from `constants.scad`. Default: vertical.
739// align = Alignment of the cylinder. Use the `V_` constants from `constants.scad`. Default: centered.
740// center = If given, overrides `align`. A true value sets `align=V_CENTER`, false sets `align=ALIGN_POS`.
741//
742// Example: By Radius
743// xdistribute(30) {
744// cyl(l=40, r=10);
745// cyl(l=40, r1=10, r2=5);
746// }
747//
748// Example: By Diameter
749// xdistribute(30) {
750// cyl(l=40, d=25);
751// cyl(l=40, d1=25, d2=10);
752// }
753//
754// Example: Chamferring
755// xdistribute(60) {
756// // Shown Left to right.
757// cyl(l=40, d=40, chamfer=7); // Default chamfang=45
758// cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=false);
759// cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=true);
760// }
761//
762// Example: Rounding/Filleting
763// cyl(l=40, d=40, fillet=10);
764//
765// Example: Heterogenous Chamfers and Fillets
766// ydistribute(80) {
767// // Shown Front to Back.
768// cyl(l=40, d=40, fillet1=15, orient=ORIENT_X);
769// cyl(l=40, d=40, chamfer2=5, orient=ORIENT_X);
770// cyl(l=40, d=40, chamfer1=12, fillet2=10, orient=ORIENT_X);
771// }
772//
773// Example: Putting it all together
774// cyl(l=40, d1=25, d2=15, chamfer1=10, chamfang1=30, from_end=true, fillet2=5);
775module cyl(
776 l=undef, h=undef,
777 r=undef, r1=undef, r2=undef,
778 d=undef, d1=undef, d2=undef,
779 chamfer=undef, chamfer1=undef, chamfer2=undef,
780 chamfang=undef, chamfang1=undef, chamfang2=undef,
781 fillet=undef, fillet1=undef, fillet2=undef,
782 circum=false, realign=false, from_end=false,
783 orient=ORIENT_Z, align=V_CENTER, center=undef
784) {
785 r1 = get_radius(r1, r, d1, d, 1);
786 r2 = get_radius(r2, r, d2, d, 1);
787 l = first_defined([l, h, 1]);
788 sides = segs(max(r1,r2));
789 sc = circum? 1/cos(180/sides) : 1;
790 orient_and_align([r1*2,r1*2,l], orient, align, center=center) {
791 zrot(realign? 180/sides : 0) {
792 if (!any_defined([chamfer, chamfer1, chamfer2, fillet, fillet1, fillet2])) {
793 cylinder(h=l, r1=r1*sc, r2=r2*sc, center=true, $fn=sides);
794 } else {
795 vang = atan2(l, r1-r2)/2;
796 chang1 = 90-first_defined([chamfang1, chamfang, vang]);
797 chang2 = 90-first_defined([chamfang2, chamfang, 90-vang]);
798 cham1 = (chamfer != undef || chamfer1 != undef)?first_defined([chamfer1, chamfer]) * (from_end? 1 : tan(chang1)):undef;
799 cham2 = (chamfer != undef || chamfer2 != undef)?first_defined([chamfer2, chamfer]) * (from_end? 1 : tan(chang2)):undef;
800 fil1 = first_defined([fillet1, fillet]);
801 fil2 = first_defined([fillet2, fillet]);
802 if (chamfer != undef) {
803 assertion(chamfer <= r1, "chamfer is larger than the r1 radius of the cylinder.");
804 assertion(chamfer <= r2, "chamfer is larger than the r2 radius of the cylinder.");
805 assertion(chamfer <= l/2, "chamfer is larger than half the length of the cylinder.");
806 }
807 if (cham1 != undef) {
808 assertion(cham1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder.");
809 assertion(cham1 <= l/2, "chamfer1 is larger than half the length of the cylinder.");
810 }
811 if (cham2 != undef) {
812 assertion(cham2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder.");
813 assertion(cham2 <= l/2, "chamfer2 is larger than half the length of the cylinder.");
814 }
815 if (fillet != undef) {
816 assertion(fillet <= r1, "fillet is larger than the r1 radius of the cylinder.");
817 assertion(fillet <= r2, "fillet is larger than the r2 radius of the cylinder.");
818 assertion(fillet <= l/2, "fillet is larger than half the length of the cylinder.");
819 }
820 if (fil1 != undef) {
821 assertion(fil1 <= r1, "fillet1 is larger than the r1 radius of the cylinder.");
822 assertion(fil1 <= l/2, "fillet1 is larger than half the length of the cylinder.");
823 }
824 if (fil2 != undef) {
825 assertion(fil2 <= r2, "fillet2 is larger than the r1 radius of the cylinder.");
826 assertion(fil2 <= l/2, "fillet2 is larger than half the length of the cylinder.");
827 }
828
829 dy1 = first_defined([cham1, fil1, 0]);
830 dy2 = first_defined([cham2, fil2, 0]);
831 maxd = max(r1,r2,l);
832
833 rotate_extrude(convexity=2) {
834 hull() {
835 difference() {
836 union() {
837 difference() {
838 back(l/2) {
839 if (cham2!=undef && cham2>0) {
840 rr2 = sc * (r2 + (r1-r2)*dy2/l);
841 chlen2 = min(rr2, cham2/sin(chang2));
842 translate([rr2,-cham2]) {
843 rotate(-chang2) {
844 translate([-chlen2,-chlen2]) {
845 square(chlen2, center=false);
846 }
847 }
848 }
849 } else if (fil2!=undef && fil2>0) {
850 translate([r2-fil2*tan(vang),-fil2]) {
851 circle(r=fil2);
852 }
853 } else {
854 translate([r2-0.005,-0.005]) {
855 square(0.01, center=true);
856 }
857 }
858 }
859
860 // Make sure the corner fiddly bits never cross the X axis.
861 fwd(maxd) square(maxd, center=false);
862 }
863 difference() {
864 fwd(l/2) {
865 if (cham1!=undef && cham1>0) {
866 rr1 = sc * (r1 + (r2-r1)*dy1/l);
867 chlen1 = min(rr1, cham1/sin(chang1));
868 translate([rr1,cham1]) {
869 rotate(chang1) {
870 left(chlen1) {
871 square(chlen1, center=false);
872 }
873 }
874 }
875 } else if (fil1!=undef && fil1>0) {
876 right(r1) {
877 translate([-fil1/tan(vang),fil1]) {
878 fsegs1 = quantup(segs(fil1),4);
879 circle(r=fil1,$fn=fsegs1);
880 }
881 }
882 } else {
883 right(r1-0.01) {
884 square(0.01, center=false);
885 }
886 }
887 }
888
889 // Make sure the corner fiddly bits never cross the X axis.
890 square(maxd, center=false);
891 }
892
893 // Force the hull to extend to the axis
894 right(0.01/2) square([0.01, l], center=true);
895 }
896
897 // Clear anything left of the Y axis.
898 left(maxd/2) square(maxd, center=true);
899
900 // Clear anything right of face
901 right((r1+r2)/2) {
902 rotate(90-vang*2) {
903 fwd(maxd/2) square(maxd, center=false);
904 }
905 }
906 }
907 }
908 }
909 }
910 }
911 }
912}
913
914
915
916// Module: downcyl()
917//
918// Description:
919// Creates a cylinder aligned below the origin.
920//
921// Usage:
922// downcyl(l|h, r|d);
923// downcyl(l|h, r1|d1, r2|d2);
924//
925// Arguments:
926// l / h = Length of cylinder. (Default: 1.0)
927// r = Radius of cylinder.
928// r1 = Bottom radius of cylinder.
929// r2 = Top radius of cylinder.
930// d = Diameter of cylinder. (use instead of r)
931// d1 = Bottom diameter of cylinder.
932// d2 = Top diameter of cylinder.
933//
934// Example: Cylinder
935// downcyl(r=20, h=40);
936// Example: Cone
937// downcyl(r1=10, r2=20, h=40);
938module downcyl(r=undef, h=undef, l=undef, d=undef, d1=undef, d2=undef, r1=undef, r2=undef)
939{
940 h = first_defined([l, h, 1]);
941 down(h/2) {
942 cylinder(r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, h=h, center=true);
943 }
944}
945
946
947
948// Module: xcyl()
949//
950// Description:
951// Creates a cylinder oriented along the X axis.
952//
953// Usage:
954// xcyl(l|h, r|d, [align|center]);
955// xcyl(l|h, r1|d1, r2|d2, [align|center]);
956//
957// Arguments:
958// l / h = Length of cylinder along oriented axis. (Default: `1.0`)
959// r = Radius of cylinder.
960// r1 = Optional radius of left (X-) end of cylinder.
961// r2 = Optional radius of right (X+) end of cylinder.
962// d = Optional diameter of cylinder. (use instead of `r`)
963// d1 = Optional diameter of left (X-) end of cylinder.
964// d2 = Optional diameter of right (X+) end of cylinder.
965// align = The side of the origin to align to. Use `V_` constants from `constants.scad`. Default: `V_CENTER`
966// center = If given, overrides `align`. A `true` value sets `align=V_CENTER`, `false` sets `align=ALIGN_POS`.
967//
968// Example: By Radius
969// ydistribute(50) {
970// xcyl(l=35, r=10);
971// xcyl(l=35, r1=15, r2=5);
972// }
973//
974// Example: By Diameter
975// ydistribute(50) {
976// xcyl(l=35, d=20);
977// xcyl(l=35, d1=30, d2=10);
978// }
979module xcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=V_CENTER, center=undef)
980{
981 cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_X, align=align, center=center);
982}
983
984
985
986// Module: ycyl()
987//
988// Description:
989// Creates a cylinder oriented along the Y axis.
990//
991// Usage:
992// ycyl(l|h, r|d, [align|center]);
993// ycyl(l|h, r1|d1, r2|d2, [align|center]);
994//
995// Arguments:
996// l / h = Length of cylinder along oriented axis. (Default: `1.0`)
997// r = Radius of cylinder.
998// r1 = Radius of front (Y-) end of cone.
999// r2 = Radius of back (Y+) end of one.
1000// d = Diameter of cylinder.
1001// d1 = Diameter of front (Y-) end of one.
1002// d2 = Diameter of back (Y+) end of one.
1003// align = The side of the origin to align to. Use `V_` constants from `constants.scad`. Default: `V_CENTER`
1004// center = Overrides `align` if given. If true, `align=V_CENTER`, if false, `align=ALIGN_POS`.
1005//
1006// Example: By Radius
1007// xdistribute(50) {
1008// ycyl(l=35, r=10);
1009// ycyl(l=35, r1=15, r2=5);
1010// }
1011//
1012// Example: By Diameter
1013// xdistribute(50) {
1014// ycyl(l=35, d=20);
1015// ycyl(l=35, d1=30, d2=10);
1016// }
1017module ycyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=V_CENTER, center=undef)
1018{
1019 cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_Y, align=align, center=center);
1020}
1021
1022
1023
1024// Module: zcyl()
1025//
1026// Description:
1027// Creates a cylinder oriented along the Z axis.
1028//
1029// Usage:
1030// zcyl(l|h, r|d, [align|center]);
1031// zcyl(l|h, r1|d1, r2|d2, [align|center]);
1032//
1033// Arguments:
1034// l / h = Length of cylinder along oriented axis. (Default: 1.0)
1035// r = Radius of cylinder.
1036// r1 = Radius of front (Y-) end of cone.
1037// r2 = Radius of back (Y+) end of one.
1038// d = Diameter of cylinder.
1039// d1 = Diameter of front (Y-) end of one.
1040// d2 = Diameter of back (Y+) end of one.
1041// align = The side of the origin to align to. Use `V_` constants from `constants.scad`. Default: `V_CENTER`
1042// center = Overrides `align` if given. If true, `align=V_CENTER`, if false, `align=ALIGN_POS`.
1043//
1044// Example: By Radius
1045// xdistribute(50) {
1046// zcyl(l=35, r=10);
1047// zcyl(l=35, r1=15, r2=5);
1048// }
1049//
1050// Example: By Diameter
1051// xdistribute(50) {
1052// zcyl(l=35, d=20);
1053// zcyl(l=35, d1=30, d2=10);
1054// }
1055module zcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, align=V_CENTER, center=undef)
1056{
1057 cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=ORIENT_Z, align=align, center=center);
1058}
1059
1060
1061
1062// Module: chamferred_cylinder()
1063// Status: DEPRECATED, use `cyl(..., chamfer)` instead.
1064//
1065// Usage:
1066// chamferred_cylinder(h, r|d, chamfer|chamfedge, [top], [bottom], [center])
1067//
1068// Description:
1069// Creates a cylinder with chamferred (bevelled) edges.
1070//
1071// Arguments:
1072// h = height of cylinder. (Default: 1.0)
1073// r = radius of cylinder. (Default: 1.0)
1074// d = diameter of cylinder. (use instead of r)
1075// chamfer = radial inset of the edge chamfer. (Default: 0.25)
1076// chamfedge = length of the chamfer edge. (Use instead of chamfer)
1077// top = boolean. If true, chamfer the top edges. (Default: True)
1078// bottom = boolean. If true, chamfer the bottom edges. (Default: True)
1079// center = boolean. If true, cylinder is centered. (Default: false)
1080module chamferred_cylinder(h=1, r=undef, d=undef, chamfer=0.25, chamfedge=undef, angle=45, center=false, top=true, bottom=true)
1081{
1082 deprecate("chamf_cyl()` and `chamferred_cylinder()", "cyl()");
1083 r = get_radius(r=r, d=d, dflt=1);
1084 chamf = (chamfedge == undef)? chamfer : chamfedge * cos(angle);
1085 cyl(l=h, r=r, chamfer1=bottom? chamf : 0, chamfer2=top? chamf : 0, chamfang=angle, center=center);
1086}
1087
1088
1089
1090// Module: chamf_cyl()
1091// Status: DEPRECATED, use `cyl(..., chamfer)` instead.
1092//
1093// Usage:
1094// chamf_cyl(h, r|d, chamfer|chamfedge, [top], [bottom], [center])
1095//
1096// Description:
1097// Creates a cylinder with chamferred (bevelled) edges. Basically a shortcut of `chamferred_cylinder()`
1098//
1099// Arguments:
1100// h = height of cylinder. (Default: 1.0)
1101// r = radius of cylinder. (Default: 1.0)
1102// d = diameter of cylinder. (use instead of r)
1103// chamfer = radial inset of the edge chamfer. (Default: 0.25)
1104// chamfedge = length of the chamfer edge. (Use instead of chamfer)
1105// top = boolean. If true, chamfer the top edges. (Default: True)
1106// bottom = boolean. If true, chamfer the bottom edges. (Default: True)
1107// center = boolean. If true, cylinder is centered. (Default: false)
1108module chamf_cyl(h=1, r=undef, d=undef, chamfer=0.25, chamfedge=undef, angle=45, center=false, top=true, bottom=true)
1109 chamferred_cylinder(h=h, r=r, d=d, chamfer=chamfer, chamfedge=chamfedge, angle=angle, center=center, top=top, bottom=bottom);
1110
1111
1112// Module: filleted_cylinder()
1113// Status: DEPRECATED, use `cyl(..., fillet)` instead.
1114//
1115// Usage:
1116// filleted_cylinder(h, r|d, fillet, [center]);
1117//
1118// Description:
1119// Creates a cylinder with filletted (rounded) ends.
1120//
1121// Arguments:
1122// h = height of cylinder. (Default: 1.0)
1123// r = radius of cylinder. (Default: 1.0)
1124// d = diameter of cylinder. (Use instead of r)
1125// fillet = radius of the edge filleting. (Default: 0.25)
1126// center = boolean. If true, cylinder is centered. (Default: false)
1127module filleted_cylinder(h=1, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, fillet=0.25, center=false) {
1128 deprecate("filleted_cylinder()", "cyl()");
1129 cyl(l=h, r=r, d=d, r1=r1, r2=r2, d1=d1, d2=d2, fillet=fillet, orient=ORIENT_Z, center=center);
1130}
1131
1132
1133
1134// Module: rcylinder()
1135// Status: DEPRECATED, use `cyl(..., fillet)` instead.
1136//
1137// Usage:
1138// rcylinder(h, r|d, fillet, [center]);
1139//
1140// Description:
1141// Creates a cylinder with filletted (rounded) ends.
1142// Basically a shortcut for `filleted_cylinder()`.
1143//
1144// Arguments:
1145// h = height of cylinder. (Default: 1.0)
1146// r = radius of cylinder. (Default: 1.0)
1147// d = diameter of cylinder. (Use instead of r)
1148// fillet = radius of the edge filleting. (Default: 0.25)
1149// center = boolean. If true, cylinder is centered. (Default: false)
1150module rcylinder(h=1, r=1, r1=undef, r2=undef, d=undef, d1=undef, d2=undef, fillet=0.25, center=false) {
1151 deprecate("rcylinder()", "cyl(..., fillet)");
1152 cyl(l=h, r=r, d=d, r1=r1, r2=r2, d1=d1, d2=d2, fillet=fillet, orient=ORIENT_Z, center=center);
1153}
1154
1155
1156
1157// Module: tube()
1158//
1159// Description:
1160// Makes a hollow tube with the given outer size and wall thickness.
1161//
1162// Usage:
1163// tube(h, ir|id, wall, [realign], [orient], [align]);
1164// tube(h, or|od, wall, [realign], [orient], [align]);
1165// tube(h, ir|id, or|od, [realign], [orient], [align]);
1166// tube(h, ir1|id1, ir2|id2, wall, [realign], [orient], [align]);
1167// tube(h, or1|od1, or2|od2, wall, [realign], [orient], [align]);
1168// tube(h, ir1|id1, ir2|id2, or1|od1, or2|od2, [realign], [orient], [align]);
1169//
1170// Arguments:
1171// h = height of tube. (Default: 1)
1172// or = Outer radius of tube.
1173// or1 = Outer radius of bottom of tube. (Default: value of r)
1174// or2 = Outer radius of top of tube. (Default: value of r)
1175// od = Outer diameter of tube.
1176// od1 = Outer diameter of bottom of tube.
1177// od2 = Outer diameter of top of tube.
1178// wall = horizontal thickness of tube wall. (Default 0.5)
1179// ir = Inner radius of tube.
1180// ir1 = Inner radius of bottom of tube.
1181// ir2 = Inner radius of top of tube.
1182// id = Inner diameter of tube.
1183// id1 = Inner diameter of bottom of tube.
1184// id2 = Inner diameter of top of tube.
1185// realign = If true, rotate the tube by half the angle of one face.
1186// orient = Orientation of the tube. Use the `ORIENT_` constants from `constants.scad`. Default: vertical.
1187// align = Alignment of the tube. Use the `V_` constants from `constants.scad`. Default: centered.
1188//
1189// Example: These all Produce the Same Tube
1190// tube(h=30, or=40, wall=5);
1191// tube(h=30, ir=35, wall=5);
1192// tube(h=30, or=40, ir=35);
1193// tube(h=30, od=80, id=70);
1194// Example: These all Produce the Same Conical Tube
1195// tube(h=30, or1=40, or2=25, wall=5);
1196// tube(h=30, ir1=35, or2=20, wall=5);
1197// tube(h=30, or1=40, or2=25, ir1=35, ir2=20);
1198// Example: Circular Wedge
1199// tube(h=30, or1=40, or2=30, ir1=20, ir2=30);
1200module tube(
1201 h=1, wall=undef,
1202 r=undef, r1=undef, r2=undef,
1203 d=undef, d1=undef, d2=undef,
1204 or=undef, or1=undef, or2=undef,
1205 od=undef, od1=undef, od2=undef,
1206 ir=undef, id=undef, ir1=undef,
1207 ir2=undef, id1=undef, id2=undef,
1208 center=undef, orient=ORIENT_Z, align=ALIGN_POS,
1209 realign=false
1210) {
1211 r1 = first_defined([or1, od1==undef?undef:od1/2, r1, d1==undef?undef:d1/2, or, od==undef?undef:od/2, r, d==undef?undef:d/2, ir1==undef||wall==undef?undef:ir1+wall, id1==undef||wall==undef?undef:id1/2+wall, ir==undef||wall==undef?undef:ir+wall, id==undef||wall==undef?undef:id/2+wall]);
1212 r2 = first_defined([or2, od2==undef?undef:od2/2, r2, d2==undef?undef:d2/2, or, od==undef?undef:od/2, r, d==undef?undef:d/2, ir2==undef||wall==undef?undef:ir2+wall, id2==undef||wall==undef?undef:id2/2+wall, ir==undef||wall==undef?undef:ir+wall, id==undef||wall==undef?undef:id/2+wall]);
1213 ir1 = first_defined([ir1, id1==undef?undef:id1/2, ir, id==undef?undef:id/2, r1==undef||wall==undef?undef:r1-wall, d1==undef||wall==undef?undef:d1/2-wall, r==undef||wall==undef?undef:r-wall, d==undef||wall==undef?undef:d/2-wall]);
1214 ir2 = first_defined([ir2, id2==undef?undef:id2/2, ir, id==undef?undef:id/2, r2==undef||wall==undef?undef:r2-wall, d2==undef||wall==undef?undef:d2/2-wall, r==undef||wall==undef?undef:r-wall, d==undef||wall==undef?undef:d/2-wall]);
1215 assertion(ir1 <= r1, "Inner radius is larger than outer radius.");
1216 assertion(ir2 <= r2, "Inner radius is larger than outer radius.");
1217 sides = segs(max(r1,r2));
1218 orient_and_align([r1*2,r1*2,h], orient, align, center=center) {
1219 zrot(realign? 180/sides : 0) {
1220 difference() {
1221 cylinder(h=h, r1=r1, r2=r2, center=true, $fn=sides);
1222 if (ir1 == ir2) {
1223 cylinder(h=h+2, r1=ir1, r2=ir2, center=true);
1224 } else {
1225 cylinder(h=h+2, r=min(ir1,ir2), center=true);
1226 diff = abs(ir1-ir2);
1227 diff2 = diff*(h+1)/h;
1228 if (ir1 > ir2) {
1229 zmove(-0.5)
1230 cylinder(h=h+1, r1=ir2+diff2, r2=ir2, center=true);
1231 } else {
1232 zmove(0.5)
1233 cylinder(h=h+1, r1=ir1, r2=ir1+diff2, center=true);
1234 }
1235 }
1236 }
1237 }
1238 }
1239}
1240
1241
1242// Module: torus()
1243//
1244// Descriptiom:
1245// Creates a torus shape.
1246//
1247// Usage:
1248// torus(r|d, r2|d2, [orient], [align]);
1249// torus(or|od, ir|id, [orient], [align]);
1250//
1251// Arguments:
1252// r = major radius of torus ring. (use with of 'r2', or 'd2')
1253// r2 = minor radius of torus ring. (use with of 'r', or 'd')
1254// d = major diameter of torus ring. (use with of 'r2', or 'd2')
1255// d2 = minor diameter of torus ring. (use with of 'r', or 'd')
1256// or = outer radius of the torus. (use with 'ir', or 'id')
1257// ir = inside radius of the torus. (use with 'or', or 'od')
1258// od = outer diameter of the torus. (use with 'ir' or 'id')
1259// id = inside diameter of the torus. (use with 'or' or 'od')
1260// orient = Orientation of the torus. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
1261// align = Alignment of the torus. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
1262//
1263// Example:
1264// // These all produce the same torus.
1265// torus(r=22.5, r2=7.5);
1266// torus(d=45, d2=15);
1267// torus(or=30, ir=15);
1268// torus(od=60, id=30);
1269module torus(
1270 r=undef, d=undef,
1271 r2=undef, d2=undef,
1272 or=undef, od=undef,
1273 ir=undef, id=undef,
1274 orient=ORIENT_Z, align=V_CENTER, center=undef
1275) {
1276 orr = get_radius(r=or, d=od, dflt=1.0);
1277 irr = get_radius(r=ir, d=id, dflt=0.5);
1278 majrad = get_radius(r=r, d=d, dflt=(orr+irr)/2);
1279 minrad = get_radius(r=r2, d=d2, dflt=(orr-irr)/2);
1280 orient_and_align([(majrad+minrad)*2, (majrad+minrad)*2, minrad*2], orient, align, center=center) {
1281 rotate_extrude(convexity=4) {
1282 right(majrad) circle(minrad);
1283 }
1284 }
1285}
1286
1287
1288
1289// Section: Spheroids
1290
1291
1292// Module: staggered_sphere()
1293//
1294// Description:
1295// An alternate construction to the standard `sphere()` built-in, with different triangulation.
1296//
1297// Usage:
1298// staggered_sphere(r|d, [circum])
1299//
1300// Arguments:
1301// r = Radius of the sphere.
1302// d = Diameter of the sphere.
1303// circum = If true, circumscribes the perfect sphere of the given size.
1304//
1305// Example:
1306// staggered_sphere(d=100, circum=true, $fn=10);
1307module staggered_sphere(r=undef, d=undef, circum=false, align=V_CENTER) {
1308 r = get_radius(r=r, d=d, dflt=1);
1309 sides = segs(r);
1310 vsides = max(3, ceil(sides/2))+1;
1311 step = 360/sides;
1312 vstep = 180/(vsides-1);
1313 rr = circum? r/cos(180/sides)/cos(180/sides) : r;
1314 pts = concat(
1315 [[0,0,rr]],
1316 [
1317 for (p = [1:vsides-2], t = [0:sides-1]) let(
1318 ta = (t+(p%2/2))*step,
1319 pa = p*vstep
1320 ) spherical_to_xyz(rr, ta, pa)
1321 ],
1322 [[0,0,-rr]]
1323 );
1324 pcnt = len(pts);
1325 faces = concat(
1326 [
1327 for (i = [1:sides], j=[0,1])
1328 j? [0, i%sides+1, i] : [pcnt-1, pcnt-1-(i%sides+1), pcnt-1-i]
1329 ],
1330 [
1331 for (p = [0:vsides-4], i = [0:sides-1], j=[0,1]) let(
1332 b1 = 1+p*sides,
1333 b2 = 1+(p+1)*sides,
1334 v1 = b1+i,
1335 v2 = b1+(i+1)%sides,
1336 v3 = b2+((i+((p%2)?(sides-1):0))%sides),
1337 v4 = b2+((i+1+((p%2)?(sides-1):0))%sides)
1338 ) j? [v1,v4,v3] : [v1,v2,v4]
1339 ]
1340 );
1341 zrot((floor(sides/4)%2==1)? 180/sides : 0) polyhedron(points=pts, faces=faces);
1342}
1343
1344
1345
1346// Section: 3D Printing Shapes
1347
1348
1349// Module: teardrop2d()
1350//
1351// Description:
1352// Makes a 2D teardrop shape. Useful for extruding into 3D printable holes.
1353//
1354// Usage:
1355// teardrop2d(r|d, [ang], [cap_h]);
1356//
1357// Arguments:
1358// r = radius of circular part of teardrop. (Default: 1)
1359// d = diameter of spherical portion of bottom. (Use instead of r)
1360// ang = angle of hat walls from the Y axis. (Default: 45 degrees)
1361// cap_h = if given, height above center where the shape will be truncated.
1362//
1363// Example(2D): Typical Shape
1364// teardrop2d(r=30, ang=30);
1365// Example(2D): Crop Cap
1366// teardrop2d(r=30, ang=30, cap_h=40);
1367// Example(2D): Close Crop
1368// teardrop2d(r=30, ang=30, cap_h=20);
1369module teardrop2d(r=1, d=undef, ang=45, cap_h=undef)
1370{
1371 eps = 0.01;
1372 r = get_radius(r=r, d=d, dflt=1);
1373 cord = 2 * r * cos(ang);
1374 cord_h = r * sin(ang);
1375 tip_y = (cord/2)/tan(ang);
1376 cap_h = min((is_def(cap_h)? cap_h : tip_y+cord_h), tip_y+cord_h);
1377 cap_w = cord * (1 - (cap_h - cord_h)/tip_y);
1378 difference() {
1379 hull() {
1380 zrot(90) circle(r=r);
1381 back(cap_h-eps/2) square([max(eps,cap_w), eps], center=true);
1382 }
1383 back(r+cap_h) square(2*r, center=true);
1384 }
1385}
1386
1387
1388// Module: teardrop()
1389//
1390// Description:
1391// Makes a teardrop shape in the XZ plane. Useful for 3D printable holes.
1392//
1393// Usage:
1394// teardrop(r|d, l|h, [ang], [cap_h], [orient], [align])
1395//
1396// Arguments:
1397// r = Radius of circular part of teardrop. (Default: 1)
1398// d = Diameter of circular portion of bottom. (Use instead of r)
1399// l = Thickness of teardrop. (Default: 1)
1400// ang = Angle of hat walls from the Z axis. (Default: 45 degrees)
1401// cap_h = If given, height above center where the shape will be truncated.
1402// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
1403// align = Alignment of the shape. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
1404//
1405// Example: Typical Shape
1406// teardrop(r=30, h=10, ang=30);
1407// Example: Crop Cap
1408// teardrop(r=30, h=10, ang=30, cap_h=40);
1409// Example: Close Crop
1410// teardrop(r=30, h=10, ang=30, cap_h=20);
1411module teardrop(r=undef, d=undef, l=undef, h=undef, ang=45, cap_h=undef, orient=ORIENT_Y, align=V_CENTER)
1412{
1413 r = get_radius(r=r, d=d, dflt=1);
1414 l = first_defined([l, h, 1]);
1415 orient_and_align([r*2,r*2,l], orient, align) {
1416 linear_extrude(height=l, center=true, slices=2) {
1417 teardrop2d(r=r, ang=ang, cap_h=cap_h);
1418 }
1419 }
1420}
1421
1422
1423// Module: onion()
1424//
1425// Description:
1426// Creates a sphere with a conical hat, to make a 3D teardrop.
1427//
1428// Usage:
1429// onion(r|d, [maxang], [cap_h], [orient], [align]);
1430//
1431// Arguments:
1432// r = radius of spherical portion of the bottom. (Default: 1)
1433// d = diameter of spherical portion of bottom.
1434// cap_h = height above sphere center to truncate teardrop shape.
1435// maxang = angle of cone on top from vertical.
1436// orient = Orientation of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
1437// align = Alignment of the shape. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
1438//
1439// Example: Typical Shape
1440// onion(r=30, maxang=30);
1441// Example: Crop Cap
1442// onion(r=30, maxang=30, cap_h=40);
1443// Example: Close Crop
1444// onion(r=30, maxang=30, cap_h=20);
1445module onion(cap_h=undef, r=undef, d=undef, maxang=45, h=undef, orient=ORIENT_Z, align=V_CENTER)
1446{
1447 r = get_radius(r=r, d=d, dflt=1);
1448 h = first_defined([cap_h, h]);
1449 maxd = 3*r/tan(maxang);
1450 orient_and_align([r*2,r*2,r*2], orient, align) {
1451 rotate_extrude(convexity=2) {
1452 difference() {
1453 teardrop2d(r=r, ang=maxang, cap_h=h);
1454 left(r) square(size=[2*r,maxd], center=true);
1455 }
1456 }
1457 }
1458}
1459
1460
1461// Module: narrowing_strut()
1462//
1463// Description:
1464// Makes a rectangular strut with the top side narrowing in a triangle.
1465// The shape created may be likened to an extruded home plate from baseball.
1466// This is useful for constructing parts that minimize the need to support
1467// overhangs.
1468//
1469// Usage:
1470// narrowing_strut(w, l, wall, [ang], [orient], [align]);
1471//
1472// Arguments:
1473// w = Width (thickness) of the strut.
1474// l = Length of the strut.
1475// wall = height of rectangular portion of the strut.
1476// ang = angle that the trianglar side will converge at.
1477// orient = Orientation of the length axis of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
1478// align = Alignment of the shape. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
1479//
1480// Example:
1481// narrowing_strut(w=10, l=100, wall=5, ang=30);
1482module narrowing_strut(w=10, l=100, wall=5, ang=30, orient=ORIENT_Y, align=V_UP)
1483{
1484 h = wall + w/2/tan(ang);
1485 orient_and_align([w, h, l], orient, align, orig_orient=ORIENT_Z) {
1486 fwd(h/2) {
1487 linear_extrude(height=l, center=true, slices=2) {
1488 back(wall/2) square([w, wall], center=true);
1489 back(wall-0.001) {
1490 yscale(1/tan(ang)) {
1491 difference() {
1492 zrot(45) square(w/sqrt(2), center=true);
1493 fwd(w/2) square(w, center=true);
1494 }
1495 }
1496 }
1497 }
1498 }
1499 }
1500}
1501
1502
1503// Module: thinning_wall()
1504//
1505// Description:
1506// Makes a rectangular wall which thins to a smaller width in the center,
1507// with angled supports to prevent critical overhangs.
1508//
1509// Usage:
1510// thinning_wall(h, l, thick, [ang], [strut], [wall], [orient], [align]);
1511//
1512// Arguments:
1513// h = height of wall.
1514// l = length of wall. If given as a vector of two numbers, specifies bottom and top lengths, respectively.
1515// thick = thickness of wall.
1516// ang = maximum overhang angle of diagonal brace.
1517// strut = the width of the diagonal brace.
1518// wall = the thickness of the thinned portion of the wall.
1519// orient = Orientation of the length axis of the wall. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_X`.
1520// align = Alignment of the shape. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
1521//
1522// Example: Typical Shape
1523// thinning_wall(h=50, l=80, thick=4);
1524// Example: Trapezoidal
1525// thinning_wall(h=50, l=[80,50], thick=4);
1526module thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2, orient=ORIENT_X, align=V_CENTER)
1527{
1528 l1 = (l[0] == undef)? l : l[0];
1529 l2 = (l[1] == undef)? l : l[1];
1530
1531 trap_ang = atan2((l2-l1)/2, h);
1532 corr1 = 1 + sin(trap_ang);
1533 corr2 = 1 - sin(trap_ang);
1534
1535 z1 = h/2;
1536 z2 = max(0.1, z1 - strut);
1537 z3 = max(0.05, z2 - (thick-wall)/2*sin(90-ang)/sin(ang));
1538
1539 x1 = l2/2;
1540 x2 = max(0.1, x1 - strut*corr1);
1541 x3 = max(0.05, x2 - (thick-wall)/2*sin(90-ang)/sin(ang)*corr1);
1542 x4 = l1/2;
1543 x5 = max(0.1, x4 - strut*corr2);
1544 x6 = max(0.05, x5 - (thick-wall)/2*sin(90-ang)/sin(ang)*corr2);
1545
1546 y1 = thick/2;
1547 y2 = y1 - min(z2-z3, x2-x3) * sin(ang);
1548
1549 orient_and_align([l1, thick, h], orient, align, orig_orient=ORIENT_X) {
1550 polyhedron(
1551 points=[
1552 [-x4, -y1, -z1],
1553 [ x4, -y1, -z1],
1554 [ x1, -y1, z1],
1555 [-x1, -y1, z1],
1556
1557 [-x5, -y1, -z2],
1558 [ x5, -y1, -z2],
1559 [ x2, -y1, z2],
1560 [-x2, -y1, z2],
1561
1562 [-x6, -y2, -z3],
1563 [ x6, -y2, -z3],
1564 [ x3, -y2, z3],
1565 [-x3, -y2, z3],
1566
1567 [-x4, y1, -z1],
1568 [ x4, y1, -z1],
1569 [ x1, y1, z1],
1570 [-x1, y1, z1],
1571
1572 [-x5, y1, -z2],
1573 [ x5, y1, -z2],
1574 [ x2, y1, z2],
1575 [-x2, y1, z2],
1576
1577 [-x6, y2, -z3],
1578 [ x6, y2, -z3],
1579 [ x3, y2, z3],
1580 [-x3, y2, z3],
1581 ],
1582 faces=[
1583 [ 4, 5, 1],
1584 [ 5, 6, 2],
1585 [ 6, 7, 3],
1586 [ 7, 4, 0],
1587
1588 [ 4, 1, 0],
1589 [ 5, 2, 1],
1590 [ 6, 3, 2],
1591 [ 7, 0, 3],
1592
1593 [ 8, 9, 5],
1594 [ 9, 10, 6],
1595 [10, 11, 7],
1596 [11, 8, 4],
1597
1598 [ 8, 5, 4],
1599 [ 9, 6, 5],
1600 [10, 7, 6],
1601 [11, 4, 7],
1602
1603 [11, 10, 9],
1604 [20, 21, 22],
1605
1606 [11, 9, 8],
1607 [20, 22, 23],
1608
1609 [16, 17, 21],
1610 [17, 18, 22],
1611 [18, 19, 23],
1612 [19, 16, 20],
1613
1614 [16, 21, 20],
1615 [17, 22, 21],
1616 [18, 23, 22],
1617 [19, 20, 23],
1618
1619 [12, 13, 17],
1620 [13, 14, 18],
1621 [14, 15, 19],
1622 [15, 12, 16],
1623
1624 [12, 17, 16],
1625 [13, 18, 17],
1626 [14, 19, 18],
1627 [15, 16, 19],
1628
1629 [ 0, 1, 13],
1630 [ 1, 2, 14],
1631 [ 2, 3, 15],
1632 [ 3, 0, 12],
1633
1634 [ 0, 13, 12],
1635 [ 1, 14, 13],
1636 [ 2, 15, 14],
1637 [ 3, 12, 15],
1638 ],
1639 convexity=6
1640 );
1641 }
1642}
1643
1644
1645// Module: braced_thinning_wall()
1646//
1647// Description:
1648// Makes a rectangular wall with cross-bracing, which thins to a smaller width in the center,
1649// with angled supports to prevent critical overhangs.
1650//
1651// Usage:
1652// braced_thinning_wall(h, l, thick, [ang], [strut], [wall], [orient], [align]);
1653//
1654// Arguments:
1655// h = height of wall.
1656// l = length of wall.
1657// thick = thickness of wall.
1658// ang = maximum overhang angle of diagonal brace.
1659// strut = the width of the diagonal brace.
1660// wall = the thickness of the thinned portion of the wall.
1661// orient = Orientation of the length axis of the wall. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
1662// align = Alignment of the shape. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
1663//
1664// Example: Typical Shape
1665// braced_thinning_wall(h=50, l=100, thick=5);
1666module braced_thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2, orient=ORIENT_Y, align=V_CENTER)
1667{
1668 dang = atan((h-2*strut)/(l-2*strut));
1669 dlen = (h-2*strut)/sin(dang);
1670 orient_and_align([thick, l, h], orient, align, orig_orient=ORIENT_Y) {
1671 xrot_copies([0, 180]) {
1672 down(h/2) narrowing_strut(w=thick, l=l, wall=strut, ang=ang);
1673 fwd(l/2) xrot(-90) narrowing_strut(w=thick, l=h-0.1, wall=strut, ang=ang);
1674 intersection() {
1675 cube(size=[thick, l, h], center=true);
1676 xrot_copies([-dang,dang]) {
1677 zspread(strut/2) {
1678 scale([1,1,1.5]) yrot(45) {
1679 cube(size=[thick/sqrt(2), dlen, thick/sqrt(2)], center=true);
1680 }
1681 }
1682 cube(size=[thick, dlen, strut/2], center=true);
1683 }
1684 }
1685 }
1686 cube(size=[wall, l-0.1, h-0.1], center=true);
1687 }
1688}
1689
1690
1691
1692// Module: thinning_triangle()
1693//
1694// Description:
1695// Makes a triangular wall with thick edges, which thins to a smaller width in
1696// the center, with angled supports to prevent critical overhangs.
1697//
1698// Usage:
1699// thinning_triangle(h, l, thick, [ang], [strut], [wall], [diagonly], [orient], [align|center]);
1700//
1701// Arguments:
1702// h = height of wall.
1703// l = length of wall.
1704// thick = thickness of wall.
1705// ang = maximum overhang angle of diagonal brace.
1706// strut = the width of the diagonal brace.
1707// wall = the thickness of the thinned portion of the wall.
1708// diagonly = boolean, which denotes only the diagonal side (hypotenuse) should be thick.
1709// orient = Orientation of the length axis of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
1710// align = Alignment of the shape. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
1711// center = If true, centers shape. If false, overrides `align` with `V_UP+V_BACK`.
1712//
1713// Example: Centered
1714// thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, center=true);
1715// Example: All Braces
1716// thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, center=false);
1717// Example: Diagonal Brace Only
1718// thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, diagonly=true, center=false);
1719module thinning_triangle(h=50, l=100, thick=5, ang=30, strut=5, wall=3, diagonly=false, center=undef, orient=ORIENT_Y, align=V_CENTER)
1720{
1721 dang = atan(h/l);
1722 dlen = h/sin(dang);
1723 orient_and_align([thick, l, h], orient, align, center=center, noncentered=V_UP+V_BACK, orig_orient=ORIENT_Y) {
1724 difference() {
1725 union() {
1726 if (!diagonly) {
1727 translate([0, 0, -h/2])
1728 narrowing_strut(w=thick, l=l, wall=strut, ang=ang);
1729 translate([0, -l/2, 0])
1730 xrot(-90) narrowing_strut(w=thick, l=h-0.1, wall=strut, ang=ang);
1731 }
1732 intersection() {
1733 cube(size=[thick, l, h], center=true);
1734 xrot(-dang) yrot(180) {
1735 narrowing_strut(w=thick, l=dlen*1.2, wall=strut, ang=ang);
1736 }
1737 }
1738 cube(size=[wall, l-0.1, h-0.1], center=true);
1739 }
1740 xrot(-dang) {
1741 translate([0, 0, h/2]) {
1742 cube(size=[thick+0.1, l*2, h], center=true);
1743 }
1744 }
1745 }
1746 }
1747}
1748
1749
1750// Module: thinning_brace()
1751// Status: DEPRECATED, use `thinning_triangle(..., diagonly=true)` instead.
1752//
1753// Description:
1754// Makes a triangular wall which thins to a smaller width in the center,
1755// with angled supports to prevent critical overhangs. Basically an alias
1756// of thinning_triangle(), with diagonly=true.
1757//
1758// Usage:
1759// thinning_brace(h, l, thick, [ang], [strut], [wall], [center])
1760//
1761// Arguments:
1762// h = height of wall.
1763// l = length of wall.
1764// thick = thickness of wall.
1765// ang = maximum overhang angle of diagonal brace.
1766// strut = the width of the diagonal brace.
1767// wall = the thickness of the thinned portion of the wall.
1768module thinning_brace(h=50, l=100, thick=5, ang=30, strut=5, wall=3, center=true)
1769{
1770 deprecate("thinning_brace()", "thinning_triangle(..., diagonly=true)");
1771 thinning_triangle(h=h, l=l, thick=thick, ang=ang, strut=strut, wall=wall, diagonly=true, center=center);
1772}
1773
1774
1775// Module: sparse_strut()
1776//
1777// Description:
1778// Makes an open rectangular strut with X-shaped cross-bracing, designed to reduce
1779// the need for support material in 3D printing.
1780//
1781// Usage:
1782// sparse_strut(h, l, thick, [strut], [maxang], [max_bridge], [orient], [align])
1783//
1784// Arguments:
1785// h = height of strut wall.
1786// l = length of strut wall.
1787// thick = thickness of strut wall.
1788// maxang = maximum overhang angle of cross-braces.
1789// max_bridge = maximum bridging distance between cross-braces.
1790// strut = the width of the cross-braces.
1791// orient = Orientation of the length axis of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
1792// align = Alignment of the shape. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
1793//
1794// Example: Typical Shape
1795// sparse_strut(h=40, l=100, thick=3);
1796// Example: Thinner Strut
1797// sparse_strut(h=40, l=100, thick=3, strut=2);
1798// Example: Larger maxang
1799// sparse_strut(h=40, l=100, thick=3, strut=2, maxang=45);
1800// Example: Longer max_bridge
1801// sparse_strut(h=40, l=100, thick=3, strut=2, maxang=45, max_bridge=30);
1802module sparse_strut(h=50, l=100, thick=4, maxang=30, strut=5, max_bridge=20, orient=ORIENT_Y, align=V_CENTER)
1803{
1804 zoff = h/2 - strut/2;
1805 yoff = l/2 - strut/2;
1806
1807 maxhyp = 1.5 * (max_bridge+strut)/2 / sin(maxang);
1808 maxz = 2 * maxhyp * cos(maxang);
1809
1810 zreps = ceil(2*zoff/maxz);
1811 zstep = 2*zoff / zreps;
1812
1813 hyp = zstep/2 / cos(maxang);
1814 maxy = min(2 * hyp * sin(maxang), max_bridge+strut);
1815
1816 yreps = ceil(2*yoff/maxy);
1817 ystep = 2*yoff / yreps;
1818
1819 ang = atan(ystep/zstep);
1820 len = zstep / cos(ang);
1821
1822 orient_and_align([thick, l, h], orient, align, orig_orient=ORIENT_Y) {
1823 zspread(zoff*2)
1824 cube(size=[thick, l, strut], center=true);
1825 yspread(yoff*2)
1826 cube(size=[thick, strut, h], center=true);
1827 yspread(ystep, n=yreps) {
1828 zspread(zstep, n=zreps) {
1829 xrot( ang) cube(size=[thick, strut, len], center=true);
1830 xrot(-ang) cube(size=[thick, strut, len], center=true);
1831 }
1832 }
1833 }
1834}
1835
1836
1837// Module: sparse_strut3d()
1838//
1839// Usage:
1840// sparse_strut3d(h, w, l, [thick], [maxang], [max_bridge], [strut], [orient], [align]);
1841//
1842// Description:
1843// Makes an open rectangular strut with X-shaped cross-bracing, designed to reduce the
1844// need for support material in 3D printing.
1845//
1846// Arguments:
1847// h = Z size of strut.
1848// w = X size of strut.
1849// l = Y size of strut.
1850// thick = thickness of strut walls.
1851// maxang = maximum overhang angle of cross-braces.
1852// max_bridge = maximum bridging distance between cross-braces.
1853// strut = the width of the cross-braces.
1854// orient = Orientation of the length axis of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
1855// align = Alignment of the shape. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
1856//
1857// Example: Typical Shape
1858// sparse_strut3d(h=30, w=30, l=100);
1859// Example: Thinner strut
1860// sparse_strut3d(h=30, w=30, l=100, strut=2);
1861// Example: Larger maxang
1862// sparse_strut3d(h=30, w=30, l=100, strut=2, maxang=50);
1863// Example: Smaller max_bridge
1864// sparse_strut3d(h=30, w=30, l=100, strut=2, maxang=50, max_bridge=20);
1865module sparse_strut3d(h=50, l=100, w=50, thick=3, maxang=40, strut=3, max_bridge=30, orient=ORIENT_Y, align=V_CENTER)
1866{
1867
1868 xoff = w - thick;
1869 yoff = l - thick;
1870 zoff = h - thick;
1871
1872 xreps = ceil(xoff/yoff);
1873 yreps = ceil(yoff/xoff);
1874 zreps = ceil(zoff/min(xoff, yoff));
1875
1876 xstep = xoff / xreps;
1877 ystep = yoff / yreps;
1878 zstep = zoff / zreps;
1879
1880 cross_ang = atan2(xstep, ystep);
1881 cross_len = hypot(xstep, ystep);
1882
1883 supp_ang = min(maxang, min(atan2(max_bridge, zstep), atan2(cross_len/2, zstep)));
1884 supp_reps = floor(cross_len/2/(zstep*sin(supp_ang)));
1885 supp_step = cross_len/2/supp_reps;
1886
1887 orient_and_align([w, l, h], orient, align, orig_orient=ORIENT_Y) {
1888 intersection() {
1889 union() {
1890 ybridge = (l - (yreps+1) * strut) / yreps;
1891 xspread(xoff) sparse_strut(h=h, l=l, thick=thick, maxang=maxang, strut=strut, max_bridge=ybridge/ceil(ybridge/max_bridge));
1892 yspread(yoff) zrot(90) sparse_strut(h=h, l=w, thick=thick, maxang=maxang, strut=strut, max_bridge=max_bridge);
1893 for(zs = [0:zreps-1]) {
1894 for(xs = [0:xreps-1]) {
1895 for(ys = [0:yreps-1]) {
1896 translate([(xs+0.5)*xstep-xoff/2, (ys+0.5)*ystep-yoff/2, (zs+0.5)*zstep-zoff/2]) {
1897 zflip_copy(offset=-(zstep-strut)/2) {
1898 xflip_copy() {
1899 zrot(cross_ang) {
1900 down(strut/2) {
1901 cube([strut, cross_len, strut], center=true);
1902 }
1903 if (zreps>1) {
1904 back(cross_len/2) {
1905 zrot(-cross_ang) {
1906 down(strut) upcube([strut, strut, zstep+strut]);
1907 }
1908 }
1909 }
1910 for (soff = [0 : supp_reps-1] ) {
1911 yflip_copy() {
1912 back(soff*supp_step) {
1913 skew_xy(ya=supp_ang) {
1914 upcube([strut, strut, zstep]);
1915 }
1916 }
1917 }
1918 }
1919 }
1920 }
1921 }
1922 }
1923 }
1924 }
1925 }
1926 }
1927 cube([w,l,h], center=true);
1928 }
1929 }
1930}
1931
1932
1933// Module: corrugated_wall()
1934//
1935// Description:
1936// Makes a corrugated wall which relieves contraction stress while still
1937// providing support strength. Designed with 3D printing in mind.
1938//
1939// Usage:
1940// corrugated_wall(h, l, thick, [strut], [wall], [orient], [align]);
1941//
1942// Arguments:
1943// h = height of strut wall.
1944// l = length of strut wall.
1945// thick = thickness of strut wall.
1946// strut = the width of the cross-braces.
1947// wall = thickness of corrugations.
1948// orient = Orientation of the length axis of the shape. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Y`.
1949// align = Alignment of the shape. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
1950//
1951// Example: Typical Shape
1952// corrugated_wall(h=50, l=100);
1953// Example: Wider Strut
1954// corrugated_wall(h=50, l=100, strut=8);
1955// Example: Thicker Wall
1956// corrugated_wall(h=50, l=100, strut=8, wall=3);
1957module corrugated_wall(h=50, l=100, thick=5, strut=5, wall=2, orient=ORIENT_Y, align=V_CENTER)
1958{
1959 amplitude = (thick - wall) / 2;
1960 period = min(15, thick * 2);
1961 steps = quantup(segs(thick/2),4);
1962 step = period/steps;
1963 il = l - 2*strut + 2*step;
1964 orient_and_align([thick, l, h], orient, align, orig_orient=ORIENT_Y) {
1965 linear_extrude(height=h-2*strut+0.1, slices=2, convexity=ceil(2*il/period), center=true) {
1966 polygon(
1967 points=concat(
1968 [for (y=[-il/2:step:il/2]) [amplitude*sin(y/period*360)-wall/2, y] ],
1969 [for (y=[il/2:-step:-il/2]) [amplitude*sin(y/period*360)+wall/2, y] ]
1970 )
1971 );
1972 }
1973
1974 difference() {
1975 cube([thick, l, h], center=true);
1976 cube([thick+0.5, l-2*strut, h-2*strut], center=true);
1977 }
1978 }
1979}
1980
1981
1982// Section: Miscellaneous
1983
1984
1985// Module: nil()
1986//
1987// Description:
1988// Useful when you MUST pass a child to a module, but you want it to be nothing.
1989module nil() union() {}
1990
1991
1992// Module: noop()
1993//
1994// Description:
1995// Passes through the children passed to it, with no action at all.
1996// Useful while debugging when you want to replace a command.
1997module noop() children();
1998
1999
2000// Module: pie_slice()
2001//
2002// Description:
2003// Creates a pie slice shape.
2004//
2005// Usage:
2006// pie_slice(ang, l|h, r|d, [orient], [align|center]);
2007// pie_slice(ang, l|h, r1|d1, r2|d2, [orient], [align|center]);
2008//
2009// Arguments:
2010// ang = pie slice angle in degrees.
2011// h = height of pie slice.
2012// r = radius of pie slice.
2013// r1 = bottom radius of pie slice.
2014// r2 = top radius of pie slice.
2015// d = diameter of pie slice.
2016// d1 = bottom diameter of pie slice.
2017// d2 = top diameter of pie slice.
2018// orient = Orientation of the pie slice. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
2019// align = Alignment of the pie slice. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
2020// center = If given, overrides `align`. A true value sets `align=V_CENTER`, false sets `align=ALIGN_POS`.
2021//
2022// Example: Cylindrical Pie Slice
2023// pie_slice(ang=45, l=20, r=30);
2024// Example: Conical Pie Slice
2025// pie_slice(ang=60, l=20, d1=50, d2=70);
2026module pie_slice(
2027 ang=30, l=undef,
2028 r=undef, r1=undef, r2=undef,
2029 d=undef, d1=undef, d2=undef,
2030 orient=ORIENT_Z, align=ALIGN_POS,
2031 center=undef, h=undef
2032) {
2033 l = first_defined([l, h, 1]);
2034 r1 = get_radius(r1, r, d1, d, 10);
2035 r2 = get_radius(r2, r, d2, d, 10);
2036 maxd = max(r1,r2)+0.1;
2037 orient_and_align([2*r1, 2*r1, l], orient, align, center=center) {
2038 difference() {
2039 cylinder(r1=r1, r2=r2, h=l, center=true);
2040 if (ang<180) rotate(ang) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
2041 difference() {
2042 fwd(maxd/2) cube([2*maxd, maxd, l+0.2], center=true);
2043 if (ang>180) rotate(ang-180) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
2044 }
2045 }
2046 }
2047}
2048
2049
2050// Module: interior_fillet()
2051//
2052// Description:
2053// Creates a shape that can be unioned into a concave joint between two faces, to fillet them.
2054// Center this part along the concave edge to be chamferred and union it in.
2055//
2056// Usage:
2057// interior_fillet(l, r, [ang], [overlap], [orient], [align]);
2058//
2059// Arguments:
2060// l = length of edge to fillet.
2061// r = radius of fillet.
2062// ang = angle between faces to fillet.
2063// overlap = overlap size for unioning with faces.
2064// orient = Orientation of the fillet. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_X`.
2065// align = Alignment of the fillet. Use the `V_` or `ALIGN_` constants from `constants.scad`. Default: `V_CENTER`.
2066//
2067// Example:
2068// union() {
2069// translate([0,2,-4]) upcube([20, 4, 24]);
2070// translate([0,-10,-4]) upcube([20, 20, 4]);
2071// color("green") interior_fillet(l=20, r=10, orient=ORIENT_XNEG);
2072// }
2073//
2074// Example:
2075// interior_fillet(l=40, r=10, orient=ORIENT_Y_90);
2076module interior_fillet(l=1.0, r=1.0, ang=90, overlap=0.01, orient=ORIENT_X, align=V_CENTER) {
2077 dy = r/tan(ang/2);
2078 orient_and_align([l,r,r], orient, align, orig_orient=ORIENT_X) {
2079 difference() {
2080 translate([0,-overlap/tan(ang/2),-overlap]) {
2081 if (ang == 90) {
2082 translate([0,r/2,r/2]) cube([l,r,r], center=true);
2083 } else {
2084 rotate([90,0,90]) pie_slice(ang=ang, r=dy+overlap, h=l, center=true);
2085 }
2086 }
2087 translate([0,dy,r]) xcyl(l=l+0.1, r=r);
2088 }
2089 }
2090}
2091
2092
2093
2094// Module: slot()
2095//
2096// Description:
2097// Makes a linear slot with rounded ends, appropriate for bolts to slide along.
2098//
2099// Usage:
2100// slot(h, l, r|d, [orient], [align|center]);
2101// slot(h, p1, p2, r|d, [orient], [align|center]);
2102// slot(h, l, r1|d1, r2|d2, [orient], [align|center]);
2103// slot(h, p1, p2, r1|d1, r2|d2, [orient], [align|center]);
2104//
2105// Arguments:
2106// p1 = center of starting circle of slot.
2107// p2 = center of ending circle of slot.
2108// l = length of slot along the X axis.
2109// h = height of slot shape. (default: 10)
2110// r = radius of slot circle. (default: 5)
2111// r1 = bottom radius of slot cone.
2112// r2 = top radius of slot cone.
2113// d = diameter of slot circle.
2114// d1 = bottom diameter of slot cone.
2115// d2 = top diameter of slot cone.
2116//
2117// Example: Between Two Points
2118// slot([0,0,0], [50,50,0], r1=5, r2=10, h=5);
2119// Example: By Length
2120// slot(l=50, r1=5, r2=10, h=5);
2121module slot(
2122 p1=undef, p2=undef, h=10, l=undef,
2123 r=undef, r1=undef, r2=undef,
2124 d=undef, d1=undef, d2=undef
2125) {
2126 r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=5);
2127 r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=5);
2128 sides = quantup(segs(max(r1, r2)), 4);
2129 hull() spread(p1=p1, p2=p2, l=l, n=2) cyl(l=h, r1=r1, r2=r2, center=true, $fn=sides);
2130}
2131
2132
2133// Module: arced_slot()
2134//
2135// Description:
2136// Makes an arced slot, appropriate for bolts to slide along.
2137//
2138// Usage:
2139// arced_slot(h, r|d, sr|sd, [sa], [ea], [orient], [align|center], [$fn2]);
2140// arced_slot(h, r|d, sr1|sd1, sr2|sd2, [sa], [ea], [orient], [align|center], [$fn2]);
2141//
2142// Arguments:
2143// cp = centerpoint of slot arc. (default: [0, 0, 0])
2144// h = height of slot arc shape. (default: 1.0)
2145// r = radius of slot arc. (default: 0.5)
2146// d = diameter of slot arc. (default: 1.0)
2147// sr = radius of slot channel. (default: 0.5)
2148// sd = diameter of slot channel. (default: 0.5)
2149// sr1 = bottom radius of slot channel cone. (use instead of sr)
2150// sr2 = top radius of slot channel cone. (use instead of sr)
2151// sd1 = bottom diameter of slot channel cone. (use instead of sd)
2152// sd2 = top diameter of slot channel cone. (use instead of sd)
2153// sa = starting angle. (Default: 0.0)
2154// ea = ending angle. (Default: 90.0)
2155// orient = Orientation of the arced slot. Use the `ORIENT_` constants from `constants.scad`. Default: `ORIENT_Z`.
2156// align = Alignment of the arced slot. Use the `V_` constants from `constants.scad`. Default: `V_CENTER`.
2157// center = If true, centers vertically. If false, drops flush with XY plane. Overrides `align`.
2158// $fn2 = The $fn value to use on the small round endcaps. The major arcs are still based on $fn. Default: $fn
2159//
2160// Example: Typical Arced Slot
2161// arced_slot(d=60, h=5, sd=10, sa=60, ea=280);
2162// Example: Conical Arced Slot
2163// arced_slot(r=60, h=5, sd1=10, sd2=15, sa=45, ea=180);
2164module arced_slot(
2165 r=undef, d=undef, h=1.0,
2166 sr=undef, sr1=undef, sr2=undef,
2167 sd=undef, sd1=undef, sd2=undef,
2168 sa=0, ea=90, cp=[0,0,0],
2169 orient=ORIENT_Z, align=V_CENTER,
2170 $fn2 = undef
2171) {
2172 r = get_radius(r=r, d=d, dflt=2);
2173 sr1 = get_radius(sr1, sr, sd1, sd, 2);
2174 sr2 = get_radius(sr2, sr, sd2, sd, 2);
2175 fn_minor = first_defined([$fn2, $fn]);
2176 da = ea - sa;
2177 orient_and_align([r+sr1, r+sr1, h], orient, align) {
2178 translate(cp) {
2179 zrot(sa) {
2180 difference() {
2181 pie_slice(ang=da, l=h, r1=r+sr1, r2=r+sr2, orient=ORIENT_Z, align=V_CENTER);
2182 cylinder(h=h+0.1, r1=r-sr1, r2=r-sr2, center=true);
2183 }
2184 right(r) cylinder(h=h, r1=sr1, r2=sr2, center=true, $fn=fn_minor);
2185 zrot(da) right(r) cylinder(h=h, r1=sr1, r2=sr2, center=true, $fn=fn_minor);
2186 }
2187 }
2188 }
2189}
2190
2191
2192
2193// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap